#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
from sklearn.neural_network import MLPClassifier
from sklearn.externals import joblib
import json
import os
from copy import deepcopy


def gensvg():
    """
    生成svg格式的棋盘, 覆盖保存到 board.svg
    """

    f = open("board.svg", "w")
    f.writelines(
        '<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="450px" height="450px" role="img" xmlns="http://www.w3.org/2000/svg">\n'
    )
    for i in range(0, 15):
        lineH = '<line x1="%d" x2="%d" y1="%d" y2="%d" stroke="#000" />\n' % (
            15, 435, 14 + 30 * i, 16 + 30 * i)
        lineV = '<line x1="%d" x2="%d" y1="%d" y2="%d" stroke="#000" />\n' % (
            14 + 30 * i, 16 + 30 * i, 15, 435)
        f.writelines(lineH)
        f.writelines(lineV)

    for i in range(0, 15):
        for j in range(0, 15):
            circle = '<circle id="%d" cx="%d" cy="%d" r="13" />\n' % (
                15 * i + j, 15 + 30 * i, 15 + 30 * j)
            f.writelines(circle)

    f.writelines('</svg>')
    f.close()


def newGame():
    """ 新开一局 """
    return np.zeros((1, 15 * 15), dtype='int8')


def winner(vec):
    """
    通过输入数组计算胜者, 取子矩阵和核矩阵外积求和
    0 - 继续
    2 - 和
    1 - 黑
    7 - 白
    """
    mat = np.zeros((19, 19))
    mat[0:15, 0:15] = np.mat(vec).reshape((15, 15))
    ks = list()
    kernel = np.zeros((5, 5))
    kernel[:, 0] = 1
    ks.append(kernel)
    kernel = np.zeros((5, 5))
    kernel[0, :] = 1
    ks.append(kernel)
    ks.append(np.eye(5))
    ks.append(np.rot90(np.eye(5)))
    for i in range(0, 15):
        for j in range(0, 15):
            Mat = mat[i:i + 5, j:j + 5]
            for k in ks:
                s = np.sum(k * Mat)
                # 黑胜
                if s == 5:
                    return 1
                # 白胜
                elif s == 35:
                    return 7

    # 继续
    if np.sum(mat) < 897:
        return 0
    # 和棋
    else:
        return 2


def turn(vec):
    """
    轮次判断
    1 - 黑
    7 - 白
    """
    s = np.sum(vec)
    if s % 2 > 0:
        return 7
    else:
        return 1


class Gomoku:
    B = 1
    W = 7

    def __init__(self, color=None):
        if color == Gomoku.W:
            self.color = color
        else:
            self.color = Gomoku.B

        self.nn = MLPClassifier(
            hidden_layer_sizes=(100, 100, 100, 100, 100, 100, 100, 100),
            max_iter=1)
        self.nn.fit(
            np.ones((1, 225), dtype='int8'), np.ones((1, 225), dtype='int8'))
        self.score = 0

    def next(self, vec):
        """ 下一步落子 """
        if turn(vec) != self.color:
            return vec

        vec = vec.reshape((1, 225))
        prob = self.nn.predict_proba(vec)
        prob = prob * (vec == 0)
        s = np.argsort(prob)
        vec[0, s[0, -1]] = self.color
        return vec

    def save(self, path):
        return joblib.dump(self, path)


def vs(b, w):
    """ 
    一次对局, b 执黑
    黑胜积 10 分, 和积 5 分, 负积 0 分
    白胜积 100 分, 和积 20 分, 负积 1 分
    """
    b.color = Gomoku.B
    w.color = Gomoku.W
    x = newGame()

    for i in range(0, 225):
        win = winner(x)
        if win == 0:
            color = turn(x)
            if color == b.color:
                x = b.next(x)
            else:
                x = w.next(x)

        elif win == 1:
            b.score += 10
            w.score += 1
            break
        elif win == 2:
            b.score += 5
            w.score += 20
            break
        elif win == 7:
            b.score += 0
            w.score += 100
            break

    print("vs Done.")
    return (b, w)


def rating(b, w):
    """ 二分杂交 """
    w1 = b.nn.coefs_
    w2 = w.nn.coefs_

    p = int(len(w1) / 2)
    w12 = w1[:p] + w2[p:]
    w21 = w2[:p] + w1[p:]
    b.nn.coefs_ = w12
    w.nn.coefs_ = w21
    return (b, w)


def mutation(b):
    """ 突变 """
    w = b.nn.coefs_
    i = np.random.randint(len(w))
    (x, y) = w[i].shape
    w[i] = np.random.randn(x, y)
    return b


def ga32(players):
    """ 32 个选手遗传 """
    # 积分清 0
    for p in players:
        p.score = 0

    # 复制 2 组
    newPlayer = list()
    for i in range(0, 2):
        newPlayer.append(players[i])

    # 杂交 30 组
    for i in range(0, 15):
        p1 = players[i + 2]
        p2 = players[31 - i]
        (p1, p2) = rating(p1, p2)
        newPlayer.append(p1)
        newPlayer.append(p2)

    players = newPlayer
    # 两个一组, 分别执黑比赛
    for i in range(0, 32):
        (players[i], players[31 - i]) = vs(players[i], players[31 - i])
    # 按照适应度排序
    players.sort(key=lambda x: x.score, reverse=True)
    l = int(len(players) / 2)
    # 淘汰一半
    players = players[:l]
    # 选一个突变
    r = np.random.randint(2, 16)
    players[r] = mutation(players[r])
    # 复制
    players = players + deepcopy(players)
    return players


def array2json(arr):
    """ np.array to json """
    encoder = json.JSONEncoder()
    return encoder.encode(arr.tolist())


def json2array(jstr, dt='int8'):
    """ json to np.array with dtype = dt """
    decoder = json.JSONDecoder()
    return np.array(decoder.decode(jstr), dtype=dt)
